#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "VObject.h"

#include "Class.h"
#include "Menu.h"
#include "Window.h"
#include "WindowSystem.h"
#include "String.h"
#include "PrintManager.h"
#include "ProgEnv.h"
#include "OrdColl.h"
#include "View.h"
#include "Look.h"
#include "Math.h"
#include "PictPort.h"
#include "Picture.h"

//const Point gBorder(4, 2);
const Point gBorder(4, 0);

const int cAutoSize = -1;

bool testopen= FALSE;

//---- VObject -----------------------------------------------------------------

NewAbstractMetaImpl(VObject,EvtHandler, (TP(container), T(contentRect)));

VObject::VObject(const Rectangle &r, int id)
			: container(0), contentRect(r), EvtHandler(id)
{
	SetFlag(eVObjDefault);
	if (!testopen)
		SetFlag(eVObjOpen);
}

VObject::VObject(const Point &e, int id)
			: container(0), contentRect(e), EvtHandler(id)
{
	SetFlag(eVObjDefault);
	if (!testopen)
		SetFlag(eVObjOpen);
}

VObject::VObject(int id) : container(0), EvtHandler(id)
{
	SetFlag(eVObjDefault);
	if (!testopen)
		SetFlag(eVObjOpen);
}

void VObject::FreeAll()
{
	VObjectIter next(this);
	Object *op;

	while (op= next())
		op->FreeAll();
}

VObject::~VObject()
{
	VObject *tmp= container;
	container= 0;
	if (tmp)
		tmp->Remove(this);
}

int VObject::Size()
{
	return 0;
}

VObject *VObject::At(int)
{
	AbstractMethod("At");
	return 0;
}

VObject *VObject::SetAt(int, VObject*)
{
	AbstractMethod("SetAt");
	return 0;
}

Iterator *VObject::MakeIterator(bool, void *placement)
{
	return new(placement) Iterator0;
}

void VObject::Add(VObject*)
{
	AbstractMethod("Add");
}

VObject *VObject::Remove(VObject*)
{
	AbstractMethod("Remove");
	return 0;
}

void VObject::RemoveFromContainer()
{
	if (container) {
		VObject *tmp= container;
		container= 0;
		tmp->Remove(this);
	}
}

void VObject::SendDown(int id, int part, void *val)
{
	VObjectIter next(this);
	VObject *vop;

	while (vop= next())
		vop->SendDown(id, part, val);
}

void VObject::CollectParts(Collection *col)
{
	VObjectIter next(this);
	Object *op;

	while (op= next())
		col->Add(op);
}

//---- state -------------------------------------------------------------------

void VObject::Open(bool mode)
{
	SetFlag(eVObjOpen, mode);
	if (Size() > 0) {
		VObjectIter next(this);
		VObject *vop;

		if (testopen) {
			while (vop= next())
				if (vop->IsOpen() != mode)
					vop->Open(mode);
		} else {
			while (vop= next())
				vop->Open(mode);
		}
	}
	if (TestFlag(eVObjKbdFocus)) {
		Window *w= GetWindow();
		if (w) {
			Manager *m= (Manager*)w->GetNextHandler();
			if (m)  {
				if (mode) {
					if (m->GetFirstHandler() == 0) {
						m->SetFirstHandler(this);
					}
				} else {
					if (m->GetFirstHandler() == this) {
						m->SetFirstHandler(0);
					}
				}
			}
		}
	}
}

bool VObject::IsOpen()
{
	return TestFlag(eVObjOpen);
}

void VObject::Enable(bool b, bool redraw)
{
	if ((TestFlag(eVObjEnabled) != 0) != (b != 0)) {
		SetFlag(eVObjEnabled, b);
		if (redraw)
			ForceRedraw();
		if (Size() > 0) {
			VObjectIter next(this);
			VObject *vop;
			while (vop= next())
				vop->Enable(b, FALSE);
		}
	}
}

void VObject::Highlight(HighlightState hst)
{
	bool b= (hst == On);
	if (TestFlag(eVObjHighlight) != b) {
		SetFlag(eVObjHighlight, b);
		ForceRedraw();
	}
}

//---- container ---------------------------------------------------------------

void VObject::SetContainer(VObject *v)
{
	container= v;
}

void VObject::ClearContainer(VObject *v)
{
	if (container && container == v)
		container= 0;
}

VObject *VObject::FindContainerOfClass(Class *cla)
{
	register VObject *vp;

	if (cla == Meta(Window))
		vp= this;
	else
		vp= GetContainer();

	for (; vp; vp= vp->GetContainer()) {
		if (vp->IsA()->isKindOf(cla))
				return vp;
	}
	return 0;
}

Point VObject::ContainerPoint(Point p)
{
	return p;
}

Point VObject::GetPortPoint(Point p)
{
	p= ContainerPoint(p);
	if (GetContainer())
		return GetContainer()->GetPortPoint(p);
	return p;
}

Window *VObject::GetWindow()
{
	return (Window*) FindContainerOfClass(Meta(Window));
}

Manager *VObject::GetManager()
{
	Window *w= GetWindow();
	if (w)
		return (Manager*)w->GetNextHandler();
	return 0;
}

View *VObject::GetView()
{
	return (View*) FindContainerOfClass(Meta(View));
}

Clipper *VObject::Getclipper()
{
	return (Clipper*) FindContainerOfClass(Meta(Clipper));
}

Rectangle VObject::GetViewedRect()
{
	return contentRect;
}

void VObject::Focus()
{
	if (GetContainer())
		GetContainer()->Focus();
}

//---- tree walking ------------------------------------------------------------

VObject *VObject::Detect(BoolFun find, void *arg)
{
	if (Size() > 0) {
		VObject *g1, *g2;
		VObjectIter next(this);

		while (g1= next())
			if (g2= g1->Detect(find, arg))
				return g2;
	}
	if (find(this, this, arg))
		return this;
	return 0;
}

static bool Comp1(Object*, Object *op, void *v)
{
	return (bool) (((VObject*)(op))->GetId() == (int)v);
}

static bool Comp2(Object*, Object *op, void *v)
{
	VObject *vop= (VObject*) op;
	Point p= *((Point*)v);
	return vop->ContainsPoint(p - vop->ContainerPoint(gPoint0));
}

static bool Comp3(Object*, Object *op, void *v)
{
	return op->IsEqual((VObject*)v);
}

static bool Comp4(Object*, Object *op, void *v)
{
	return op == (VObject*)v;
}

VObject *VObject::FindItem(int id)
{
	if (id != cIdNone)
		return Detect(Comp1, (void*) id);
	return 0;
}

VObject *VObject::FindItem(Point p)
{
	return Detect(Comp2, &p);
}

VObject *VObject::FindItem(VObject *g)
{
	return Detect(Comp3, g);
}

VObject *VObject::FindItemPtr(VObject *g)
{
	return Detect(Comp4, g);
}

//---- layout ------------------------------------------------------------------

Metric VObject::GetMinSize()
{
	if (Size() > 0) {
		Metric m;
		VObjectIter next(this);
		VObject *dip;

		while (dip= next())
			m.Merge(dip->GetMinSize());
		return m;
	}
	return Metric(contentRect.extent);
}

void VObject::CalcExtent()
{
	Point ms(GetMinSize().Extent()),
		e(GetExtent());
	if (e.x < ms.x)
		e.x= ms.x;
	if (e.y < ms.y)
		e.y= ms.y;
	SetExtent(e);
}

int VObject::Base()
{
	return GetMinSize().base;
}

void VObject::SetOrigin(Point origin)
{
	if (contentRect.origin != origin) {
		contentRect.origin= origin;
		if (TestFlag(eVObjLayoutCntl))
			Control(GetId(), cPartOriginChanged, this);
		Object::Send(GetId(), cPartOriginChanged, &origin);
	}
}

void VObject::SetExtent(Point extent)
{
	if (contentRect.extent != extent) {
		contentRect.extent= extent;
		if (TestFlag(eVObjLayoutCntl))
			Control(GetId(), cPartExtentChanged, this);
	//  Object::Send(GetId(), cPartExtentChanged, this);
		Object::Send(GetId(), cPartExtentChanged, &extent);
	}
}

void VObject::ExtentChanged(VObject*)
{
	if (GetContainer())
		GetContainer()->ExtentChanged(this);
	//SetExtent(Max(GetMinSize().Extent(), GetExtent()));
}

void VObject::SetContentRect(const Rectangle &r, bool redraw)
{
	if (!redraw && contentRect == r)
		return;
	if (redraw)
		ForceRedraw();
	if (contentRect.extent != r.extent)
		SetExtent(r.extent);
	SetOrigin(r.origin);
	if (redraw)
		ForceRedraw();
}

void VObject::Align(const Point &aa, const Metric &m, VObjAlign a)
{
	Point at(aa);
	switch (a & eVObjH) {
	case eVObjHLeft:
		break;
	case eVObjHCenter:
		at.x+= (m.Width() - Width())/2;
		break;
	case eVObjHRight:
		at.x+= m.Width() - Width();
		break;
	}
	switch (a & eVObjV) {
	case eVObjVBase:
		at.y+= m.Base() - Base();
		break;
	case eVObjVCenter:
		at.y+= (m.Height() - Height())/2;
		break;
	case eVObjVBottom:
		at.y+= m.Height() - Height();
		break;
	}
	SetOrigin(at);
}

void VObject::Move(const Point &delta, bool redraw)
{
	if (delta == gPoint0)
		return;
	if (redraw)
		ForceRedraw();
	SetOrigin(GetOrigin()+delta);
	if (redraw) {
		ForceRedraw();
		Changed();
	}
}

//---- resizing ----------------------------------------------------------------

Command *VObject::GetMover()
{
	VObject *v= GetView();
	if (v)
		return new VObjectMover(this, v->contentRect);
	return new VObjectMover(this);
}

Command *VObject::GetStretcher()
{
	VObject *v= GetView();
	if (v)
		return new VObjectStretcher(this, v->contentRect);
	return new VObjectStretcher(this);
}

bool VObject::ContainsPoint(Point p)
{
	return contentRect.ContainsPoint(p);
}

//---- drawing -----------------------------------------------------------------

void VObject::DrawAll(Rectangle r, bool highlight)
{
	if (IsOpen() && r.Clip(contentRect))
		DrawInner(r, (bool) (highlight || TestFlag(eVObjHighlight)));
}

void VObject::DrawInner(Rectangle r, bool highlight)
{
	if (!GrHasColor())
		Draw(r);
	if (highlight && ! gPrinting)
		DrawHighlight(r);
	if (GrHasColor())
		Draw(r);
}

void VObject::Draw(Rectangle r)
{
	if (Size() > 0) {
		VObjectIter next(this);
		VObject *vop;

		while (vop= next())
			vop->DrawAll(r, FALSE);
	}
}

void VObject::DrawHighlight(Rectangle)
{
	gLook->DrawHighlight(contentRect);
}

void VObject::Outline2(Point p1, Point p2)
{
	GrStrokeRect(NormRect(p1, p2));
}

void VObject::OutlineRect(const Rectangle &r)
{
	Outline2(r.NW(), r.SE());
}

void VObject::Outline(const Point &delta)
{
	OutlineRect(contentRect+delta);
}

void VObject::RevealRect(Rectangle r, Point minToSee)
{
	if (GetContainer())
		GetContainer()->RevealRect(r, minToSee);
}

void VObject::RevealAlign(Rectangle r, VObjAlign al)
{
	if (GetContainer())
		GetContainer()->RevealAlign(r, al);
}

void VObject::Scroll(int mode, Point scroll, bool redraw)
{
	if (GetContainer())
		GetContainer()->Scroll(mode, scroll, redraw);
}

//---- invalidation and focusing -----------------------------------------------

void VObject::InvalidateRect(Rectangle r)
{
	if (r.Clip(contentRect) && IsOpen() && GetContainer())
		GetContainer()->InvalidateViewRect(r);
}

void VObject::InvalidateViewRect(Rectangle r)
{
	r.origin= ContainerPoint(r.origin);
	InvalidateRect(r);
}

void VObject::ForceRedraw()
{
	InvalidateRect(contentRect);
}

void VObject::UpdateEvent()
{
	gWindowSystem->Update();
}

GrCursor VObject::GetCursor(Point lp)
{
	if (GetContainer())
		return GetContainer()->GetCursor(ContainerPoint(lp));
	return eCrsBoldArrow;
}

//---- input/output ------------------------------------------------------------

OStream &VObject::PrintOn(OStream &s)
{
	EvtHandler::PrintOn(s);
	return s << contentRect SP;
}

IStream &VObject::ReadFrom(IStream &s)
{
	EvtHandler::ReadFrom(s);
	s >> contentRect;
	container= 0;
	return s;
}

//---- input handling ----------------------------------------------------------

EvtHandler *VObject::GetNextHandler()
{
	return GetContainer();
}

void VObject::DoTrackMouse(TrackPhase atp, Point)
{
	if (Enabled()) {
		switch (atp) {
		case eTrackPress:
			Highlight(On);
			break;
		case eTrackRelease:
			Control(GetId(), cPartToggle, (void*) 1);
		case eTrackExit:
			Highlight(Off);
			break;
		default:
			break;
		}
	}
}

void VObject::ReadEvent(Token &t)
{
	if (GetContainer())
		GetContainer()->ReadEvent(t);
}

Command *VObject::Input(Point lp, Token &t, Clipper *vf)
{
	if (t.IsKey() || t.IsFunctionKey() || t.IsCursorKey() || ContainsPoint(lp)) {
		if (Enabled()) {
			if (TestFlag(eVObjKbdFocus) && t.Code == eEvtLeftButton
												&& !(t.Flags & eFlgButDown)) {
				GetWindow()->SetFirstHandler(this);
			}
			return DispatchEvents(lp, t, vf);
		}
		return gNoChanges;
	}
	return 0;
}

Command *VObject::DispatchEvents(Point lp, Token &t, Clipper *vf)
{
	register Command *currCmd;

	if (Size() > 0) {
		VObjectIter previous(this, cIterBackward);
		VObject *dip;

		while (dip= previous())
			if (currCmd= dip->Input(lp, t, vf))
				return currCmd;
	}

	currCmd= gNoChanges;

	if (t.IsMouseButton() || t.Code == eEvtLocMove /* || t.Code == eEvtLocStill */)
		GrSetCursor(GetCursor(lp));

	switch (t.Code) {
	case eEvtRightButton:
		if (!(t.Flags & eFlgButDown))
			currCmd= DoRightButtonDownCommand(lp, t, WindowSystem::Clicks, vf);
		break;
	case eEvtMiddleButton:
	case eEvtLeftButton:
		if (!(t.Flags & eFlgButDown)) {
			if (t.Code == eEvtLeftButton) {
				if (t.Flags == (eFlgShiftKey|eFlgCntlKey|eFlgMetaKey))
					gProgEnv->InspectClick(this);
				else
					currCmd= DoLeftButtonDownCommand(lp, t, WindowSystem::Clicks);
			} else
				currCmd= DoMiddleButtonDownCommand(lp, t, WindowSystem::Clicks);
			if (currCmd && currCmd != gNoChanges && vf)
				currCmd= vf->TrackInContent(lp, t, currCmd);
		}
		break;
	default:
		if (t.IsKey())
			currCmd= DoKeyCommand(t.Code, t);
		else if (t.IsFunctionKey())
			currCmd= DoFunctionKeyCommand(t.FunctionCode(), t);
		else if (t.IsCursorKey())
			currCmd= DoCursorKeyCommand(t.CursorDir(), t);
		else
			currCmd= DoOtherEventCommand(lp, t);
		break;
	}
	return currCmd;
}

Command *VObject::DoKeyCommand(int ch, Token t)
{
	if (GetContainer())
		return GetContainer()->DoKeyCommand(ch, t);
	return gNoChanges;
}

Command *VObject::DoCursorKeyCommand(EvtCursorDir d, Token t)
{
	if (GetContainer())
		return GetContainer()->DoCursorKeyCommand(d, t);
	return gNoChanges;
}

Command *VObject::DoFunctionKeyCommand(int pfk, Token t)
{
	int code= -1;

	switch (t.FunctionCode()) {
	case 3:
		code= cUNDO;
		break;
	case 5:
		code= cCOPY;
		break;
	case 4:
		code= cTOP;
		break;
	case 7:
		code= cPASTE;
		break;
	case 9:
		code= cCUT;
		break;
	case 8:
		code= cFIND;
		break;
	case 6:
		code= cOPEN;
		break;
	}
	if (code > 0)
		return DoMenuCommand(code);
	if (GetContainer())
		return GetContainer()->DoFunctionKeyCommand(pfk, t);
	return gNoChanges;
}

Command *VObject::DoButtonDownCommand(const Point&, const Token &t,
							int clicks, Clipper*)
{
	return gNoChanges;
}

Command *VObject::DoMiddleButtonDownCommand(Point p, Token t, int clicks)
{
	if (GetContainer())
		return GetContainer()->DoMiddleButtonDownCommand(ContainerPoint(p), t, clicks);
	return gNoChanges;
}

Command *VObject::DoLeftButtonDownCommand(Point p, Token t, int clicks)
{
	if (GetContainer())
		return GetContainer()->DoLeftButtonDownCommand(ContainerPoint(p), t, clicks);
	return gNoChanges;
}

Command *VObject::DoOtherEventCommand(Point p, Token t)
{
	if (GetContainer())
		return GetContainer()->DoOtherEventCommand(ContainerPoint(p), t);
	return gNoChanges;
}

Command *VObject::DoRightButtonDownCommand(Point lp, Token, int, Clipper *vf)
{
	if (vf) {
		Menu *menu= GetMenu();
		if (menu) {
			EvtHandler *fh= 0;
			Manager *m= GetManager();
			if (m)
				fh= m->GetFirstHandler();
			int cmdno;
			if ((cmdno= menu->Pulldown(lp, vf)) >= 0) {     // show menu in window
				vf->Focus();
				if (fh)
					fh->PerformCommand(fh->DoMenuCommand(cmdno));
				else
					PerformCommand(DoMenuCommand(cmdno));
			}
		}
	}
	return gNoChanges;
}

Command *VObject::TrackInContent(Point lp, Token t, Command *currCmd)
{
	if (GetContainer())
		return GetContainer()->TrackInContent(ContainerPoint(lp), t, currCmd);
	return currCmd;
}

//---- misc --------------------------------------------------------------------

void VObject::Print()
{
	gPrintManager->ShowPrintDialog(this);
}

Picture *VObject::AsPicture()
{
	Picture *picture= new Picture;
	PictPort *pp= new PictPort(picture);
	Port *old= GrGetPort();
	GrSetPort(pp);
	DrawAll(contentRect, FALSE);
	GrSetPort(old);
	delete pp;
	return picture;
}

//---- VObjectIter -------------------------------------------------------------

VObjectIter::~VObjectIter()
{
	if (seq) {
		if ((char*)seq == space)
			seq->~Iterator();
		else
			delete seq;
		seq= 0;
	}
}

void VObjectIter::Reset(VObject *v, bool dir)
{
	if (seq) {
		if ((char*)seq == space)
			seq->~Iterator();
		else
			delete seq;
		seq= 0;
	}
	seq= v->MakeIterator(dir, space);
}

//---- VObjectCommand ----------------------------------------------------------

VObjectCommand::VObjectCommand(VObject *g) : Command(cIdNone)
{
	vop= g;
	SetFlag(eCmdNoReplFeedback);
}

void VObjectCommand::Init(VObject *g, const Rectangle &cr, const Point &gr,
										GrCursor cd, int hy)
{
	vop= g;
	constrainRect= cr;
	grid= gr;
	newcursor= cd;
	firstmove= FALSE;
	hysterese= hy;
	oldRect= newRect= vop->ContentRect();
}

Command *VObjectCommand::TrackMouse(TrackPhase atp, Point ap, Point, Point np)
{
	switch (atp) {
	case eTrackPress:
		oldcursor= GrGetCursor();
		break;

	case eTrackRelease:
		GrSetCursor(oldcursor);
		
		if (firstmove) {
			if (vop->GetView() && !TestFlag(eCmdFullScreen))
				vop->SetContentRect(oldRect, FALSE);
			break;
		}
		return gNoChanges;

	case eTrackMove:
		delta= np - ap;
		if (Math::Abs(delta.x) > hysterese || Math::Abs(delta.y) > hysterese) {
			firstmove= TRUE;
			GrSetCursor(newcursor);
		}
		break;
	default:
		break;
	}
	return this;
}

void VObjectCommand::TrackConstrain(Point ap, Point, Point *np)
{
	Rectangle r(oldRect);

	r.origin+= *np-ap;
	*np+= r.AmountToTranslateWithin(constrainRect);
	if (grid >= 1)
		*np= (*np/grid) * grid;     // align to grid
}

void VObjectCommand::TrackFeedback(Point, Point, bool on)
{
	if (firstmove) {
		if (GrGetPenInk() != ePatXor) {
			if (on)
				vop->SetContentRect(newRect, TRUE);
		} else
			vop->Outline2(newRect.NW(), newRect.SE());
	}
}

void VObjectCommand::DoIt()
{
	vop->SetContentRect(newRect, !TestFlag(eCmdFullScreen));
}

void VObjectCommand::UndoIt()
{
	vop->SetContentRect(oldRect, !TestFlag(eCmdFullScreen));
}

//---- VObjectMover ------------------------------------------------------------

VObjectMover::VObjectMover(VObject *g) : VObjectCommand(g)
{
	Init(g, gRect0, gPoint1, eCrsMoveHand, 2);
}

VObjectMover::VObjectMover(VObject *g, const Rectangle &cr) : VObjectCommand(g)
{
	Init(g, cr, gPoint1, eCrsMoveHand, 2);
}

VObjectMover::VObjectMover(VObject *g, const Rectangle &cr, const Point &gr,
									GrCursor cd, int hy) : VObjectCommand(g)
{
	Init(g, cr, gr, cd, hy);
}

void VObjectMover::Init(VObject *g, const Rectangle &cr, const Point &gr,
									GrCursor cd, int hy)
{
	SetName("move");
	VObjectCommand::Init(g, cr, gr, cd, hy);
}

Command *VObjectMover::TrackMouse(TrackPhase atp, Point ap, Point pp, Point np)
{
	Command *cmd= VObjectCommand::TrackMouse(atp, ap, pp, np);
	switch (atp) {
	case eTrackMove:
		newRect= oldRect;
		newRect.origin+= delta;
		return this;
	case eTrackRelease:
		if (newRect == oldRect) {
			if (vop->IsKindOf(Window))
				((Window*)vop)->Top();
			cmd= gNoChanges;
		}
		break;
	default:
		break;
	}
	return cmd;
}

//---- VObjectStretcher --------------------------------------------------------

VObjectStretcher::VObjectStretcher(VObject *g) : VObjectCommand(g)
{
	Init(g, gRect0, gPoint1, eCrsMoveStretch, 2, g->GetMinSize().extent);
}

VObjectStretcher::VObjectStretcher(VObject *g, const Rectangle &cr)
													: VObjectCommand(g)
{
	Init(g, cr, gPoint1, eCrsMoveStretch, 2, g->GetMinSize().extent);
}

VObjectStretcher::VObjectStretcher(VObject *g, const Rectangle &cr,
									const Point &ms) : VObjectCommand(g)
{
	Init(g, cr, ms, eCrsMoveStretch, 2, g->GetMinSize().extent);
}

VObjectStretcher::VObjectStretcher(VObject *g, const Rectangle &cr,
	const Point &ms, const Point &gr, GrCursor cd, int hy) : VObjectCommand(g)
{
	Init(g, cr,  gr, cd, hy, ms);
}

void VObjectStretcher::Init(VObject *g, const Rectangle &cr,
				const Point &gr, GrCursor cd, int hy, const Point &ms)
{
	SetName("resize");
	VObjectCommand::Init(g, cr, gr, cd, hy);
	minSize= ms;
}

void VObjectStretcher::TrackConstrain(Point ap, Point, Point *np)
{
	switch (corner) {
	case 0: case 4:
		np->x= ap.x;
		break;
	case 2: case 6:
		np->y= ap.y;
		break;
	}
}

Command *VObjectStretcher::TrackMouse(TrackPhase atp, Point ap, Point pp, Point np)
{
	switch (atp) {
	case eTrackPress:
		VObjectCommand::TrackMouse(atp, ap, pp, np);
		corner= oldRect.PointToCorner(ap);
		break;

	case eTrackMove:
		{
		VObjectCommand::TrackMouse(atp, ap, pp, np);
		Point p1(oldRect.NW()), p2(oldRect.SE());

		switch (corner) {
		case 0: case 6: case 7:
			p1+= delta;
			break;
		case 1:
			p2.x+= delta.x;
			p1.y+= delta.y;
			break;
		case 5:
			p1.x+= delta.x;
			p2.y+= delta.y;
			break;
		default: // case 2: case 3: case 4:
			p2+= delta;
			break;
		}
		newRect= NormRect(p1, p2);
		newRect.extent= Max(newRect.extent, minSize);
		}
		break;

	case eTrackRelease:
		return VObjectCommand::TrackMouse(atp, ap, pp, np);

	default:
		break;
	}
	return this;
}

